使用 Systemd Timer 调度任务
Systemd 提供 timer 已经有一段时间了,作为 cron 的替代品,它值得一试。本篇文章将会告诉你如何使用 systemd 中的 timer 在系统启动后重复执行任务。这不是对 systemd 的详细讨论,只是对 timer 组件的介绍。
Cron vs anacron vs systemd:快速查看
Cron 可以以几分钟到几个月或更长时间的粒度运行。设置相对简单,只需要一个配置文件,尽管配置略微复杂,但是普通用户也能使用。
但是如果您的系统在任务的特定的时间点上不是开机状态,那么这次任务就会被跳过去
Anacron克服了“系统未运行”的问题。它确保当您的系统再次处于活动状态时将执行任务。虽然它旨在供管理员使用,但某些系统为一般用户提供访问权限。
但是,Anacron 的时间粒度最低是以天为单位。
cron 和 anacron 都有执行上下文必须一致的问题。任务运行时的环境和测试时的环境需要相同,必须提供相同的 shell、环境变量和路径。这意味着测试和调试有时很困难。
Systemd timer 具备 cron 和 anacron 的优秀能力,不仅允许调度粒度精细到分钟,而且允许错过时间点后执行任务。而且可供所有用户使用。
但是配置比较复杂,至少需要两个配置文件。
如果您的 cron 和 anacron 已经配置的很好了,那么可能没有理由去转换代码。但是 systemd 依然值得去研究,因为它可以简化 cron/anacron 的方案。
配置
Systemd timer 至少需要两个配置文件:定时器单元和服务单元。对于多行命令,你还需要一个“作业”文件或脚本。
定时器单元定义了调度方式,服务单元定义了执行的人物。有关定时器单元的详细信息,请参阅 systemd.timer
,对于服务单元的详细信息,参见 systemd.service
。
单元文件存在于多个位置(在手册页中列出)。然而,对于普通用户而言,最简单的位置是 ~/.config/systemd/user
。请注意:此处的 “user”是字符串“user”
演示
这个演示是一个简单的例子,它创建一个用户调度作业,而不是系统调度作业(以 root 身份运行)。它将消息、日期和时间打印到文件中。
首先创建一个执行任务的脚本,并将其存放到本地的“bin”目录:
touch ~/bin/schedule-test.sh
然后添加以下内容到您刚刚创建的文件中:
#!/bin/sh echo "This is only a test: $(date)" >> "$HOME/schedule-test-output.txt"
记得给予其执行权。
创建服务单元
~/.config/systemd/user/schedule-test.service
:Description=A job to test the systemd scheduler Type=simple ExecStart=/home/<user>/bin/schedule-test.sh WantedBy=default.target
请注意:<user> 应当是您的 @HOME 地址。但是单元文件路径名中的“user”就是字符串“user”
ExecStart 应当提供的是一个没有变量的绝对地址。但对于用户单元,您可以使用“%h”替换 $HOME 。也就是说,您可以使用:
ExecStart=%h/bin/schedule-test.sh
这只能用于用户单元文件而不是系统单元,因为“%h”在系统环境中始终返回“/root”。其他替换可以在
systemd.unit
中的“SPECIFIERS”下找到。创建一个定时器文件
~/.config/systemd/user/schedule-test.timer
,它将调度您刚刚创建的服务单元: 。请注意,其与服务单元唯一的区别是拓展名不同。Description=Schedule a message every 1 minute RefuseManualStart=no # Allow manual starts RefuseManualStop=no # Allow manual stops #Execute job if it missed a run due to machine being off Persistent=true #Run 120 seconds after boot for the first time OnBootSec=120 #Run every 1 minute thereafter OnUnitActiveSec=60 #File describing job to execute Unit=schedule-test.service WantedBy=timers.target
注意定时器文件使用“OnUnitActiveSec”指定调度。更灵活的方式是使用“OnCalendar”选项。例如:
# run on the minute of every minute every hour of every day OnCalendar=*-*-* *:*:00 # run on the hour of every hour of every day OnCalendar=*-*-* *:00:00 # run every day OnCalendar=*-*-* 00:00:00 # run 11:12:13 of the first or fifth day of any month of the year # 2012, but only if that day is a Thursday or Friday OnCalendar=Thu,Fri 2012-*-1,5 11:12:13
更多“OnCalendar”相关的信息请看 这里
所有必要的工作都已经完成了,但是你应当首先测试一下。首先启用用户服务:
$ systemctl --user enable schedule-test.service
这将有以下输出:
Created symlink /home/<user>/.config/systemd/user/default.target.wants/schedule-test.service → /home/<user>/.config/systemd/user/schedule-test.service.
然后进行一次测试:
$ systemctl --user start schedule-test.service
检查您的输出文件($HOME/schedule-test-output.txt)确保你的脚本执行正确无误。
作业正常工作后,我们启用并启动用户计时器:
$ systemctl --user enable --now schedule-test.timer
注意:您在上述步骤中已经启用了服务,因此这里只需要启用定时器即可。
这将产生类似下面的输出:
Created symlink /home/<user>/.config/systemd/user/timers.target.wants/schedule-test.timer → /home/<user>/.config/systemd/user/schedule-test.timer.
其他操作
您可以检查和监控服务。如果您收到来自服务单元的错误,下面的第一个命令特别有用:
$ systemctl --user status schedule-test $ systemctl --user list-unit-files
手动停止服务:
$ systemctl --user stop schedule-test.service
永久停止和禁用计时器和服务,重新加载守护程序配置并重置任何失败通知:
$ systemctl --user stop schedule-test.timer $ systemctl --user disable schedule-test.timer $ systemctl --user stop schedule-test.service $ systemctl --user disable schedule-test.service $ systemctl --user daemon -reload $ systemctl --user reset-failed
概括
这篇文章将引导您使用systemd计时器,但是,systemd 的内容远不止这里介绍的内容。本文应该为您提供相关的基础。您可以从 Fedora 杂志 systemd 系列 开始探索更多相关信息 。
systemd.timer
systemd.service
另外,https://opensource.com/ 有一个很好的 systemd 备忘单